Introduction

Hello! My name is Hira Anees Awan. I am from Pakistan. Currently, I am pursuing a Master’s degree in Biostatistics at Duke University. I recently joined Tomaras Lab to work with Cesar Lopez. I love to teach and do computer programming so this workshop is kind of like the best case scenario for me.

You can email me at for any queries or input. I would love your feedback on this workshop.

Let’s get started!

You will be wondering what is this beautiful notebook like thing where I can write paragraphs and insert chunks of code and run it like a script. Notice the extension of this file. It is ‘.Rmd’. This means it is an R Markdown Notebook. When you execute code within the notebook, the results appear beneath the code.

Here, I am providing a link to a brief cheatsheet to R markdown. You can do all kinds of cool stuff with R markdown. Today’s main agenda is not to teach you R markdown but I felt like this is a great tool to introduce to beginners who like to document every single line of code. And, it is fun too.

Code Chunks

Let’s add a code chunk and try to run it in R.

2+2
[1] 4

Viola! Two plus two equals four. Great! Now that everything is up and running, I will explain what we are trying to do today.

Agenda

The main agenda of today’s workshop is to get you acquainted with basics of Data Science using R. I must add that we are going to focus on small datasets. I believe a beginner’s workshop should focus on smaller datasets that are easy to manipulate and visualize.

At the conclusion of this workshop, you will be able:


Loading data into R


You can almost load any kind of file in R. We will focus on the widely used files including text files and csv.

Text file - Tab delimited
  • Loading a text file into an R Dataframe…
  • ‘header=TRUE’ means that the names of the columns are included in the text file
  • ‘\t’ specifies that the file is tab-delimited.

  • head shows the first six rows of a data frame

TextFile_df <- read.table("Tab_Delimited_Text_File.txt", header = TRUE, sep = "\t")
head(TextFile_df)
csv file
  • Loading csv file into an R Dataframe…
  • ‘header=TRUE’ means that the names of the columns are included in the text file

  • tail shows the last six rows of a data frame

train_df <- read.csv("train.csv",header = TRUE)
test_df <- read.csv("test.csv",header = TRUE)

head(train_df)
tail(test_df)

Here, I would like to stop and emphasize on some datatypes in R which you will come across very often.


Vector: It will have one datatype only. If you do not put the same datatype, it will enforce the same datatype across all the elements of the vector.

vector_1 <- c('Biostatistics','Electrical Engineering','Mechanical Engineering')
print(vector_1)
[1] "Biostatistics"          "Electrical Engineering" "Mechanical Engineering"
vector_2 <- c('Covid', 10+9)
print(vector_1[1])
[1] "Biostatistics"
print(vector_2[2])
[1] "19"

List: It can have different datatypes.

# Create a list.
list_1 <- list(c(1,2,3),45,'Quarantine')
print(list_1)
[[1]]
[1] 1 2 3

[[2]]
[1] 45

[[3]]
[1] "Quarantine"
print(list_1[[1]])
[1] 1 2 3

Matrices: A matrix is a two-dimensional rectangular data set. It can be created using a vector input to the matrix function.

matrix_1 = matrix( c('h','i','r','a','9',5), nrow = 2, ncol = 3, byrow = TRUE)
print(matrix_1)
     [,1] [,2] [,3]
[1,] "h"  "i"  "r" 
[2,] "a"  "9"  "5" 
print(matrix_1[1,])
[1] "h" "i" "r"
print(matrix_1[,2])
[1] "i" "9"
print(matrix_1[1,2])
[1] "i"

Arrays: While matrices are confined to two dimensions, arrays can be of any number of dimensions. The array function takes a dim attribute which creates the required number of dimension. In the below example we create an array with two elements which are 3x3 matrices each

# Create an array.
array_1 <- array(c('orange','green'),dim = c(3,3,4))
print(array_1)
, , 1

     [,1]     [,2]     [,3]    
[1,] "orange" "green"  "orange"
[2,] "green"  "orange" "green" 
[3,] "orange" "green"  "orange"

, , 2

     [,1]     [,2]     [,3]    
[1,] "green"  "orange" "green" 
[2,] "orange" "green"  "orange"
[3,] "green"  "orange" "green" 

, , 3

     [,1]     [,2]     [,3]    
[1,] "orange" "green"  "orange"
[2,] "green"  "orange" "green" 
[3,] "orange" "green"  "orange"

, , 4

     [,1]     [,2]     [,3]    
[1,] "green"  "orange" "green" 
[2,] "orange" "green"  "orange"
[3,] "green"  "orange" "green" 
#first matrix
print(array_1[,,1])
     [,1]     [,2]     [,3]    
[1,] "orange" "green"  "orange"
[2,] "green"  "orange" "green" 
[3,] "orange" "green"  "orange"
#third column of first matrix
print(array_1[,3,1]) 
[1] "orange" "green"  "orange"
#third row of second matrix
print(array_1[3,,2])
[1] "green"  "orange" "green" 

Factors: For categorical data

# Create a vector.
apple_colors <- c('green','green','yellow','red','red','red','green')
# Create a factor object.
factor_1 <- factor(apple_colors)
# Print the factor.
print(factor_1)
[1] green  green  yellow red    red    red    green 
Levels: green red yellow
print(nlevels(factor_1))
[1] 3

Let’s get back on track and work towards step 2 that is…


Data manipulation


Using Dplyr

Selecting columns of a dataframe using dplyr

library(dplyr)
package 㤼㸱dplyr㤼㸲 was built under R version 3.6.3Registered S3 method overwritten by 'dplyr':
  method           from
  print.rowwise_df     

Attaching package: 㤼㸱dplyr㤼㸲

The following objects are masked from 㤼㸱package:stats㤼㸲:

    filter, lag

The following objects are masked from 㤼㸱package:base㤼㸲:

    intersect, setdiff, setequal, union
somecolumns_1 <- select(train_df, Name, Sex, Age)
head(somecolumns_1)

# keep the variables name and all variables 
# between parch and cabin inclusive
somecolumns_2 <- select(train_df,Parch:Cabin)
head(somecolumns_2)

# keep all variables except Embarked
somecolumns_3 <- select(train_df, -Embarked)
head(somecolumns_3)

Selecting rows of a dataframe using dplyr

library(dplyr)
somerows_1 <- filter(train_df, 
                  Sex == "female")
head(somerows_1)

Mutating a dataframe using dplyr

#Changing Fare variable by multiplying each entry by 1.1
mutated_1 <- mutate(train_df, Fare = Fare * 1.1);
#Creating a new variable called FareCategorical that is low when Fare < 70 and #high otherwise
mutated_2 <- mutate(train_df, 
                  FareCategorical = ifelse(Fare < 70, 'low','high'))

Using pipes Pipes are used to do multiple steps in one go. Here, first, we will filter the dataset by females. Resulting instances will be females, the next step is to group all the females by class. Now, all the females will be classified based on the Pclass variable. Last step is to create a variable called mean_age that will compute the mean of age in the sub groups of these females.

pipe_1 <- train_df %>%
  filter(Sex == "female") %>%
  group_by(Pclass) %>%
  summarize(mean_age = mean(Age, na.rm = TRUE))

Descriptive statistics


This is a very short section. We will look at how R can present summaries of different variables.

Summarizing a continous variable

summary(train_df$Age)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
   0.42   20.12   28.00   29.70   38.00   80.00     177 
#Try running this ... Will not return a value...
mean(train_df$Age)
[1] NA
#Why? It has missing values... Solution? Remove the missing values. 
mean(train_df$Age, na.rm=TRUE)
[1] 29.69912

Summarizing a categorical variable

table(train_df$Sex)

female   male 
   314    577 

Data Visualization


ggplot

ggplot is one of the most famous libraries of R that is used for data visualizations. If you want to learn more about ggplots, here is a link for you.

library(ggplot2)
package 㤼㸱ggplot2㤼㸲 was built under R version 3.6.3
ggplot(data = train_df,
       mapping = aes(x = PassengerId, y = Fare))

Why is the graph empty? We specified that the PassengerId variable should be mapped to the x-axis and that the Fare should be mapped to the y-axis, but we haven’t yet specified what we wanted placed on the graph.

Geoms to the rescue!

library(ggplot2)
ggplot(data = train_df,
       mapping = aes(x = PassengerId, y = Fare))+
  geom_point()

Let’s make it fancy and fit a line!

library(ggplot2)
ggplot(data = train_df,
       mapping = aes(x = PassengerId, y = Fare))+
  geom_point(color = "cornflowerblue",
             alpha = .7,
             size = 3)+
   geom_smooth(method = "lm")

Histograms

library(scales)
package 㤼㸱scales㤼㸲 was built under R version 3.6.3
ggplot(train_df, 
       aes(x = Age, 
           y= ..count.. / sum(..count..))) +
  geom_histogram(fill = "Blue", 
                 color = "white", 
                 binwidth = 5) + 
  labs(title="Travellers by age", 
       y = "Percent",
       x = "Age") +
  scale_y_continuous(labels = percent)

Categorical data

ggplot(train_df, aes(x = Sex)) + 
  geom_bar(fill = "cornflowerblue", 
           color="black") +
  labs(x = "Sex", 
       y = "Frequency", 
       title = "Travellers by Sex")+
  coord_flip()

Pie chart

# create a pie chart with slice labels
plotdata <- train_df %>%
  count(Pclass) %>%
  arrange(desc(Pclass)) %>%
  mutate(prop = round(n*100/sum(n), 1),
         lab.ypos = cumsum(prop) - 0.5*prop)

plotdata$label <- paste0(plotdata$Pclass, "\n",
                         round(plotdata$prop), "%")

ggplot(plotdata, 
       aes(x = "", 
           y = prop, 
           fill = Pclass)) +
  geom_bar(width = 1, 
           stat = "identity", 
           color = "black") +
  geom_text(aes(y = lab.ypos, label = label), 
            color = "black") +
  coord_polar("y", 
              start = 0, 
              direction = -1) +
  theme_void() +
  theme(legend.position = "FALSE") +
  labs(title = "Participants by class")

Tree map

library(treemapify)
package 㤼㸱treemapify㤼㸲 was built under R version 3.6.3
# create a treemap of marriage officials
plotdata <- train_df %>%
  count(Pclass)

ggplot(plotdata, 
       aes(fill = Pclass, 
           area = n,
           label = Pclass)) +
  geom_treemap() + 
  geom_treemap_text(colour = "white", 
                    place = "centre") +
   labs(title = "Training data by class") +
  theme(legend.position = "none")

Visualizing 3 variables

# plot fare histograms by Pclass
ggplot(train_df, aes(x = Fare)) +
  geom_histogram(fill = "cornflowerblue",
                 color = "white") +
  facet_wrap(~Pclass, ncol = 1) +
  labs(title = "Fare histograms by Pclass")

# plot fare histograms by sex and pclass
ggplot(train_df, aes(x = Fare)) +
  geom_histogram(color = "white",
                 fill = "cornflowerblue") +
  facet_grid(Sex ~ Pclass) +
  labs(title = "Fare histograms by sex and pclass",
       x = "Fare")

Miscellaneous graphs

library(ggplot2)
library(plotly)
package 㤼㸱plotly㤼㸲 was built under R version 3.6.3Registered S3 method overwritten by 'data.table':
  method           from
  print.data.table     
Registered S3 methods overwritten by 'htmltools':
  method               from         
  print.html           tools:rstudio
  print.shiny.tag      tools:rstudio
  print.shiny.tag.list tools:rstudio
Registered S3 method overwritten by 'htmlwidgets':
  method           from         
  print.htmlwidget tools:rstudio

Attaching package: 㤼㸱plotly㤼㸲

The following object is masked from 㤼㸱package:ggplot2㤼㸲:

    last_plot

The following object is masked from 㤼㸱package:stats㤼㸲:

    filter

The following object is masked from 㤼㸱package:graphics㤼㸲:

    layout
p <- ggplot(train_df, aes(x=PassengerId, 
                     y=Fare, 
                     color=Pclass)) +
  geom_point(size=3) +
  labs(x = "Passenger Id",
       y = "Fare",
       color = "Passenger Class") +
  theme_bw()

ggplotly(p)

Data Analysis


Neural Networks

Interesting video that beautifully explains what a neural network is… link

# load library
require(neuralnet)
Loading required package: neuralnet
package 㤼㸱neuralnet㤼㸲 was built under R version 3.6.3
Attaching package: 㤼㸱neuralnet㤼㸲

The following object is masked from 㤼㸱package:dplyr㤼㸲:

    compute
m <- model.matrix( 
  ~ Survived + Pclass + Sex + Age + SibSp + Parch + Fare + Embarked, 
  data = train_df 
)
head(m)
  (Intercept) Survived Pclass Sexmale Age SibSp Parch    Fare EmbarkedC
1           1        0      3       1  22     1     0  7.2500         0
2           1        1      1       0  38     1     0 71.2833         1
3           1        1      3       0  26     0     0  7.9250         0
4           1        1      1       0  35     1     0 53.1000         0
5           1        0      3       1  35     0     0  8.0500         0
7           1        0      1       1  54     0     0 51.8625         0
  EmbarkedQ EmbarkedS
1         0         1
2         0         0
3         0         1
4         0         1
5         0         1
7         0         1
library(neuralnet)
r <- neuralnet( 
  Survived ~ Pclass + Sexmale + Age + SibSp + Parch + Fare + EmbarkedC + EmbarkedQ + EmbarkedS, 
  data=m, hidden=10, threshold=0.5
)
plot(r)

m2 <- model.matrix( 
  ~ Pclass + Sex + Age + SibSp + Parch + Fare + Embarked, 
  data = test_df 
)

Predict=compute(r,m2)
prob <- Predict$net.result
New_test_df <-test_df %>% drop_na(Pclass, Sex, Age,SibSp,Parch, Fare,Embarked)
New_test_df$Survived <-ifelse(prob>0.5, 1, 0)
write.csv(New_test_df,"Result.csv", row.names = FALSE)
Principal Component Analysis (PCA)

PCA is a famous dimensionality reduction technique and you want to find that which component/set of components introduce the most variation in your response variable. In other words, we are interested in finding those variables which are mathematically the richest in terms of information.

Note: PCA can only be run on continuous variables.

library(tidyr)
train_pca_df <- select(train_df, Age, SibSp, Parch, Fare)
train_pca_df <- train_pca_df %>% drop_na()
pca_result <- prcomp(train_pca_df, center = TRUE,scale. = TRUE)
summary(pca_result)
Importance of components:
                          PC1    PC2    PC3    PC4
Standard deviation     1.2794 1.0522 0.8182 0.7659
Proportion of Variance 0.4092 0.2768 0.1673 0.1467
Cumulative Proportion  0.4092 0.6860 0.8533 1.0000

Interpretation:

  • 40.92% of the total variation in data can be explained by age.
  • 27.68% of the total variation in data can be explained by SibSp
  • 16.73% of the total variation in data can be explained by Parch.
  • 14.67% of the total variation in data can be explained by Fare.

I will definitely keep age in the model, when I am using other machine learning algorithms because the variable age introduces the highest percentage of total variation in the given dataset.

LS0tDQp0aXRsZTogIkJhc2ljcyBvZiBEYXRhIFNjaWVuY2UgaW4gUiINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCioqKg0KDQojIyMjICoqSW50cm9kdWN0aW9uKioNCkhlbGxvISBNeSBuYW1lIGlzICpIaXJhIEFuZWVzIEF3YW4qLiBJIGFtIGZyb20gKlBha2lzdGFuKi4gQ3VycmVudGx5LCBJIGFtIHB1cnN1aW5nIGEgTWFzdGVyJ3MgZGVncmVlIGluIEJpb3N0YXRpc3RpY3MgYXQgRHVrZSBVbml2ZXJzaXR5LiBJIHJlY2VudGx5IGpvaW5lZCBUb21hcmFzIExhYiB0byB3b3JrIHdpdGggQ2VzYXIgTG9wZXouIEkgbG92ZSB0byB0ZWFjaCBhbmQgZG8gY29tcHV0ZXIgcHJvZ3JhbW1pbmcgc28gdGhpcyB3b3Jrc2hvcCBpcyBraW5kIG9mIGxpa2UgdGhlIGJlc3QgY2FzZSBzY2VuYXJpbyBmb3IgbWUuDQoNCllvdSBjYW4gZW1haWwgbWUgYXQgaGE5NkBkdWtlLmVkdSBmb3IgYW55IHF1ZXJpZXMgb3IgaW5wdXQuIEkgd291bGQgbG92ZSB5b3VyIGZlZWRiYWNrIG9uIHRoaXMgd29ya3Nob3AuDQoNCiMjIyMjICoqTGV0J3MgZ2V0IHN0YXJ0ZWQhKioNCg0KWW91IHdpbGwgYmUgd29uZGVyaW5nIHdoYXQgaXMgdGhpcyBiZWF1dGlmdWwgbm90ZWJvb2sgbGlrZSB0aGluZyB3aGVyZSBJIGNhbiB3cml0ZSBwYXJhZ3JhcGhzIGFuZCBpbnNlcnQgY2h1bmtzIG9mIGNvZGUgYW5kIHJ1biBpdCBsaWtlIGEgc2NyaXB0LiBOb3RpY2UgdGhlIGV4dGVuc2lvbiBvZiB0aGlzIGZpbGUuIEl0IGlzICoqJy5SbWQnKiouIFRoaXMgbWVhbnMgaXQgaXMgYW4gW1IgTWFya2Rvd25dKGh0dHA6Ly9ybWFya2Rvd24ucnN0dWRpby5jb20pIE5vdGVib29rLiBXaGVuIHlvdSBleGVjdXRlIGNvZGUgd2l0aGluIHRoZSBub3RlYm9vaywgdGhlIHJlc3VsdHMgYXBwZWFyIGJlbmVhdGggdGhlIGNvZGUuIA0KDQpIZXJlLCBJIGFtIHByb3ZpZGluZyBhIFtsaW5rXShodHRwczovL3JzdHVkaW8uY29tL3dwLWNvbnRlbnQvdXBsb2Fkcy8yMDE1LzAyL3JtYXJrZG93bi1jaGVhdHNoZWV0LnBkZikgdG8gYSBicmllZiBjaGVhdHNoZWV0IHRvIFIgbWFya2Rvd24uIFlvdSBjYW4gZG8gYWxsIGtpbmRzIG9mIGNvb2wgc3R1ZmYgd2l0aCBSIG1hcmtkb3duLiBUb2RheSdzIG1haW4gYWdlbmRhIGlzIG5vdCB0byB0ZWFjaCB5b3UgUiBtYXJrZG93biBidXQgSSBmZWx0IGxpa2UgdGhpcyBpcyBhIGdyZWF0IHRvb2wgdG8gaW50cm9kdWNlIHRvIGJlZ2lubmVycyB3aG8gbGlrZSB0byBkb2N1bWVudCBldmVyeSBzaW5nbGUgbGluZSBvZiBjb2RlLiBBbmQsIGl0IGlzIGZ1biB0b28uIA0KDQoNCiMjIyMjICoqQ29kZSBDaHVua3MqKg0KTGV0J3MgYWRkIGEgY29kZSBjaHVuayBhbmQgdHJ5IHRvIHJ1biBpdCBpbiBSLiANCg0KYGBge3J9DQoyKzINCmBgYA0KDQpWaW9sYSEgVHdvIHBsdXMgdHdvIGVxdWFscyBmb3VyLiBHcmVhdCEgTm93IHRoYXQgZXZlcnl0aGluZyBpcyB1cCBhbmQgcnVubmluZywgSSB3aWxsIGV4cGxhaW4gd2hhdCB3ZSBhcmUgdHJ5aW5nIHRvIGRvIHRvZGF5LiANCg0KIyMjIyAqKkFnZW5kYSoqDQpUaGUgbWFpbiBhZ2VuZGEgb2YgdG9kYXkncyB3b3Jrc2hvcCBpcyB0byBnZXQgeW91IGFjcXVhaW50ZWQgd2l0aCBiYXNpY3Mgb2YgRGF0YSBTY2llbmNlIHVzaW5nIFIuIEkgbXVzdCBhZGQgdGhhdCB3ZSBhcmUgZ29pbmcgdG8gZm9jdXMgb24gc21hbGwgZGF0YXNldHMuIEkgYmVsaWV2ZSBhIGJlZ2lubmVyJ3Mgd29ya3Nob3Agc2hvdWxkIGZvY3VzIG9uIHNtYWxsZXIgZGF0YXNldHMgdGhhdCBhcmUgZWFzeSB0byBtYW5pcHVsYXRlIGFuZCB2aXN1YWxpemUuIA0KDQpBdCB0aGUgY29uY2x1c2lvbiBvZiB0aGlzIHdvcmtzaG9wLCB5b3Ugd2lsbCBiZSBhYmxlOg0KDQoqIFRvIGxvYWQgdGV4dCBmaWxlcyBhbmQgY3N2IGZpbGVzIGludG8gUg0KKiBUbyB1bmRlcnN0YW5kIGFuZCB1c2UgZGlmZmVyZW50IGRhdGEgdHlwZXMgaW4gUiB3aWRlbHkgdXNlZCBpbiBEYXRhIFNjaWVuY2UNCiogVG8gdXNlIGFuIFIgbGlicmFyeQ0KKiBUbyBtYW5pcHVsYXRlIGRhdGEgdXNpbmcgcG93ZXJmdWwgUiBsaWJyYXJpZXMNCiogVG8gY2FsY3VsYXRlIGRlc2NyaXB0aXZlIHN0YXRpc3RpY3Mgb24gZGlmZmVyZW50IGtpbmRzIG9mIGZlYXR1cmVzIGluIGEgZGF0YQ0KKiBUbyB2aXN1YWxpemUgZGlmZmVyZW50IGZlYXR1cmVzIG9mIGEgZGF0YXNldCB1c2luZyBhIHZhcmlldHkgb2YgcGxvdHMNCiogVG8gYW5hbHl6ZSBkYXRhIHVzaW5nIGFuIFIgbGlicmFyeSAoSSBhbSBub3QgcHJpbWFyaWx5IGZvY3VzaW5nIG9uIGRhdGEgYW5hbHlzaXMgcGFydCBiZWNhdXNlIHRoZXJlIGlzIGEgaHVnZSB2YXJpZXR5IG9mIGFsZ29yaXRobXMgYXZhaWxhYmxlIGZvciBkaWZmZXJlbnQga2luZHMgb2YgZGF0YSBhbmQgdGhlIHR5cGUgb2YgcXVlc3Rpb24geW91IGFyZSB0cnlpbmcgdG8gYW5zd2VyIHVzaW5nIHRoYXQgZGF0YS4pDQoqIEJvbnVzOiBOZXVyYWwgbmV0d29ya3MgaW4gUiwgUHJpbmNpcGFsIENvbXBvbmVudCBBbmFseXNpcyBpbiBSDQoNCioqKg0KIyMjIyAqKkxvYWRpbmcgZGF0YSBpbnRvIFIqKg0KKioqDQpZb3UgY2FuIGFsbW9zdCBsb2FkIGFueSBraW5kIG9mIGZpbGUgaW4gUi4gV2Ugd2lsbCBmb2N1cyBvbiB0aGUgd2lkZWx5IHVzZWQgZmlsZXMgaW5jbHVkaW5nIHRleHQgZmlsZXMgYW5kIGNzdi4gDQoNCiMjIyMjICoqVGV4dCBmaWxlIC0gVGFiIGRlbGltaXRlZCoqDQoNCisgTG9hZGluZyBhIHRleHQgZmlsZSBpbnRvIGFuIFIgRGF0YWZyYW1lLi4uDQorICdoZWFkZXI9VFJVRScgbWVhbnMgdGhhdCB0aGUgbmFtZXMgb2YgdGhlIGNvbHVtbnMgYXJlIGluY2x1ZGVkIGluIHRoZSB0ZXh0IGZpbGUNCisgJ1xcdCcgc3BlY2lmaWVzIHRoYXQgdGhlIGZpbGUgaXMgdGFiLWRlbGltaXRlZC4NCg0KKyBoZWFkIHNob3dzIHRoZSBmaXJzdCBzaXggcm93cyBvZiBhIGRhdGEgZnJhbWUgDQpgYGB7cn0NClRleHRGaWxlX2RmIDwtIHJlYWQudGFibGUoIlRhYl9EZWxpbWl0ZWRfVGV4dF9GaWxlLnR4dCIsIGhlYWRlciA9IFRSVUUsIHNlcCA9ICJcdCIpDQpoZWFkKFRleHRGaWxlX2RmKQ0KYGBgDQoNCg0KIyMjIyMgKipjc3YgZmlsZSoqDQoNCisgTG9hZGluZyBjc3YgZmlsZSBpbnRvIGFuIFIgRGF0YWZyYW1lLi4uDQorICdoZWFkZXI9VFJVRScgbWVhbnMgdGhhdCB0aGUgbmFtZXMgb2YgdGhlIGNvbHVtbnMgYXJlIGluY2x1ZGVkIGluIHRoZSB0ZXh0IGZpbGUNCg0KKyB0YWlsIHNob3dzIHRoZSBsYXN0IHNpeCByb3dzIG9mIGEgZGF0YSBmcmFtZQ0KDQpgYGB7cn0NCnRyYWluX2RmIDwtIHJlYWQuY3N2KCJ0cmFpbi5jc3YiLGhlYWRlciA9IFRSVUUpDQp0ZXN0X2RmIDwtIHJlYWQuY3N2KCJ0ZXN0LmNzdiIsaGVhZGVyID0gVFJVRSkNCg0KaGVhZCh0cmFpbl9kZikNCnRhaWwodGVzdF9kZikNCmBgYA0KDQoNCkhlcmUsIEkgd291bGQgbGlrZSB0byBzdG9wIGFuZCBlbXBoYXNpemUgb24gc29tZSBkYXRhdHlwZXMgaW4gUiB3aGljaCB5b3Ugd2lsbCBjb21lIGFjcm9zcyB2ZXJ5IG9mdGVuLiANCg0KKioqDQoNCioqVmVjdG9yOiBJdCB3aWxsIGhhdmUgb25lIGRhdGF0eXBlIG9ubHkuIElmIHlvdSBkbyBub3QgcHV0IHRoZSBzYW1lIGRhdGF0eXBlLCBpdCB3aWxsIGVuZm9yY2UgdGhlIHNhbWUgZGF0YXR5cGUgYWNyb3NzIGFsbCB0aGUgZWxlbWVudHMgb2YgdGhlIHZlY3Rvci4gICoqDQoNCmBgYHtyfQ0KdmVjdG9yXzEgPC0gYygnQmlvc3RhdGlzdGljcycsJ0VsZWN0cmljYWwgRW5naW5lZXJpbmcnLCdNZWNoYW5pY2FsIEVuZ2luZWVyaW5nJykNCnByaW50KHZlY3Rvcl8xKQ0KdmVjdG9yXzIgPC0gYygnQ292aWQnLCAxMCs5KQ0KcHJpbnQodmVjdG9yXzFbMV0pDQpwcmludCh2ZWN0b3JfMlsyXSkNCmBgYA0KDQoqKiogDQoNCioqTGlzdDogSXQgY2FuIGhhdmUgZGlmZmVyZW50IGRhdGF0eXBlcy4gKioNCmBgYHtyfQ0KIyBDcmVhdGUgYSBsaXN0Lg0KbGlzdF8xIDwtIGxpc3QoYygxLDIsMyksNDUsJ1F1YXJhbnRpbmUnKQ0KcHJpbnQobGlzdF8xKQ0KcHJpbnQobGlzdF8xW1sxXV0pDQpgYGANCg0KKioqIA0KDQoqKk1hdHJpY2VzOiBBIG1hdHJpeCBpcyBhIHR3by1kaW1lbnNpb25hbCByZWN0YW5ndWxhciBkYXRhIHNldC4gSXQgY2FuIGJlIGNyZWF0ZWQgdXNpbmcgYSB2ZWN0b3IgaW5wdXQgdG8gdGhlIG1hdHJpeCBmdW5jdGlvbi4gKioNCg0KYGBge3J9DQptYXRyaXhfMSA9IG1hdHJpeCggYygnaCcsJ2knLCdyJywnYScsJzknLDUpLCBucm93ID0gMiwgbmNvbCA9IDMsIGJ5cm93ID0gVFJVRSkNCnByaW50KG1hdHJpeF8xKQ0KcHJpbnQobWF0cml4XzFbMSxdKQ0KcHJpbnQobWF0cml4XzFbLDJdKQ0KcHJpbnQobWF0cml4XzFbMSwyXSkNCmBgYA0KDQoqKiogDQoNCioqQXJyYXlzOiBXaGlsZSBtYXRyaWNlcyBhcmUgY29uZmluZWQgdG8gdHdvIGRpbWVuc2lvbnMsIGFycmF5cyBjYW4gYmUgb2YgYW55IG51bWJlciBvZiBkaW1lbnNpb25zLiBUaGUgYXJyYXkgZnVuY3Rpb24gdGFrZXMgYSBkaW0gYXR0cmlidXRlIHdoaWNoIGNyZWF0ZXMgdGhlIHJlcXVpcmVkIG51bWJlciBvZiBkaW1lbnNpb24uIEluIHRoZSBiZWxvdyBleGFtcGxlIHdlIGNyZWF0ZSBhbiBhcnJheSB3aXRoIHR3byBlbGVtZW50cyB3aGljaCBhcmUgM3gzIG1hdHJpY2VzIGVhY2ggKioNCg0KYGBge3J9DQojIENyZWF0ZSBhbiBhcnJheS4NCmFycmF5XzEgPC0gYXJyYXkoYygnb3JhbmdlJywnZ3JlZW4nKSxkaW0gPSBjKDMsMyw0KSkNCnByaW50KGFycmF5XzEpDQojZmlyc3QgbWF0cml4DQpwcmludChhcnJheV8xWywsMV0pDQojdGhpcmQgY29sdW1uIG9mIGZpcnN0IG1hdHJpeA0KcHJpbnQoYXJyYXlfMVssMywxXSkgDQojdGhpcmQgcm93IG9mIHNlY29uZCBtYXRyaXgNCnByaW50KGFycmF5XzFbMywsMl0pDQoNCmBgYA0KDQoqKioNCioqRmFjdG9yczogRm9yIGNhdGVnb3JpY2FsIGRhdGEqKg0KDQpgYGB7cn0NCiMgQ3JlYXRlIGEgdmVjdG9yLg0KYXBwbGVfY29sb3JzIDwtIGMoJ2dyZWVuJywnZ3JlZW4nLCd5ZWxsb3cnLCdyZWQnLCdyZWQnLCdyZWQnLCdncmVlbicpDQojIENyZWF0ZSBhIGZhY3RvciBvYmplY3QuDQpmYWN0b3JfMSA8LSBmYWN0b3IoYXBwbGVfY29sb3JzKQ0KIyBQcmludCB0aGUgZmFjdG9yLg0KcHJpbnQoZmFjdG9yXzEpDQoNCnByaW50KG5sZXZlbHMoZmFjdG9yXzEpKQ0KYGBgDQpMZXQncyBnZXQgYmFjayBvbiB0cmFjayBhbmQgd29yayB0b3dhcmRzIHN0ZXAgMiB0aGF0IGlzLi4uDQoNCioqKg0KIyMjIyAqKkRhdGEgbWFuaXB1bGF0aW9uKioNCioqKg0KDQojIyMjIyAqKlVzaW5nIERwbHlyKioNCioqU2VsZWN0aW5nIGNvbHVtbnMgb2YgYSBkYXRhZnJhbWUgdXNpbmcgZHBseXIqKg0KYGBge3J9DQpsaWJyYXJ5KGRwbHlyKQ0KDQpzb21lY29sdW1uc18xIDwtIHNlbGVjdCh0cmFpbl9kZiwgTmFtZSwgU2V4LCBBZ2UpDQpoZWFkKHNvbWVjb2x1bW5zXzEpDQoNCiMga2VlcCB0aGUgdmFyaWFibGVzIG5hbWUgYW5kIGFsbCB2YXJpYWJsZXMgDQojIGJldHdlZW4gcGFyY2ggYW5kIGNhYmluIGluY2x1c2l2ZQ0Kc29tZWNvbHVtbnNfMiA8LSBzZWxlY3QodHJhaW5fZGYsUGFyY2g6Q2FiaW4pDQpoZWFkKHNvbWVjb2x1bW5zXzIpDQoNCiMga2VlcCBhbGwgdmFyaWFibGVzIGV4Y2VwdCBFbWJhcmtlZA0Kc29tZWNvbHVtbnNfMyA8LSBzZWxlY3QodHJhaW5fZGYsIC1FbWJhcmtlZCkNCmhlYWQoc29tZWNvbHVtbnNfMykNCmBgYA0KDQoqKlNlbGVjdGluZyByb3dzIG9mIGEgZGF0YWZyYW1lIHVzaW5nIGRwbHlyKioNCg0KYGBge3J9DQpsaWJyYXJ5KGRwbHlyKQ0Kc29tZXJvd3NfMSA8LSBmaWx0ZXIodHJhaW5fZGYsIA0KICAgICAgICAgICAgICAgICAgU2V4ID09ICJmZW1hbGUiKQ0KaGVhZChzb21lcm93c18xKQ0KYGBgDQoNCioqTXV0YXRpbmcgYSBkYXRhZnJhbWUgdXNpbmcgZHBseXIqKg0KDQpgYGB7cn0NCiNDaGFuZ2luZyBGYXJlIHZhcmlhYmxlIGJ5IG11bHRpcGx5aW5nIGVhY2ggZW50cnkgYnkgMS4xDQptdXRhdGVkXzEgPC0gbXV0YXRlKHRyYWluX2RmLCBGYXJlID0gRmFyZSAqIDEuMSk7DQojQ3JlYXRpbmcgYSBuZXcgdmFyaWFibGUgY2FsbGVkIEZhcmVDYXRlZ29yaWNhbCB0aGF0IGlzIGxvdyB3aGVuIEZhcmUgPCA3MCBhbmQgI2hpZ2ggb3RoZXJ3aXNlDQptdXRhdGVkXzIgPC0gbXV0YXRlKHRyYWluX2RmLCANCiAgICAgICAgICAgICAgICAgIEZhcmVDYXRlZ29yaWNhbCA9IGlmZWxzZShGYXJlIDwgNzAsICdsb3cnLCdoaWdoJykpDQoNCmBgYA0KDQoqKlVzaW5nIHBpcGVzKioNClBpcGVzIGFyZSB1c2VkIHRvIGRvIG11bHRpcGxlIHN0ZXBzIGluIG9uZSBnby4gSGVyZSwgZmlyc3QsIHdlIHdpbGwgZmlsdGVyIHRoZSBkYXRhc2V0IGJ5IGZlbWFsZXMuIFJlc3VsdGluZyBpbnN0YW5jZXMgd2lsbCBiZSBmZW1hbGVzLCB0aGUgbmV4dCBzdGVwIGlzIHRvIGdyb3VwIGFsbCB0aGUgZmVtYWxlcyBieSBjbGFzcy4gTm93LCBhbGwgdGhlIGZlbWFsZXMgd2lsbCBiZSBjbGFzc2lmaWVkIGJhc2VkIG9uIHRoZSBQY2xhc3MgdmFyaWFibGUuIExhc3Qgc3RlcCBpcyB0byBjcmVhdGUgYSB2YXJpYWJsZSBjYWxsZWQgbWVhbl9hZ2UgdGhhdCB3aWxsIGNvbXB1dGUgdGhlIG1lYW4gb2YgYWdlIGluIHRoZSBzdWIgZ3JvdXBzIG9mIHRoZXNlIGZlbWFsZXMuIA0KYGBge3J9DQpwaXBlXzEgPC0gdHJhaW5fZGYgJT4lDQogIGZpbHRlcihTZXggPT0gImZlbWFsZSIpICU+JQ0KICBncm91cF9ieShQY2xhc3MpICU+JQ0KICBzdW1tYXJpemUobWVhbl9hZ2UgPSBtZWFuKEFnZSwgbmEucm0gPSBUUlVFKSkNCmBgYA0KDQoqKioNCiMjIyMgKipEZXNjcmlwdGl2ZSBzdGF0aXN0aWNzKioNCioqKg0KVGhpcyBpcyBhIHZlcnkgc2hvcnQgc2VjdGlvbi4gV2Ugd2lsbCBsb29rIGF0IGhvdyBSIGNhbiBwcmVzZW50IHN1bW1hcmllcyBvZiANCmRpZmZlcmVudCB2YXJpYWJsZXMuDQoNCioqU3VtbWFyaXppbmcgYSBjb250aW5vdXMgdmFyaWFibGUqKg0KYGBge3J9DQpzdW1tYXJ5KHRyYWluX2RmJEFnZSkNCg0KI1RyeSBydW5uaW5nIHRoaXMgLi4uIFdpbGwgbm90IHJldHVybiBhIHZhbHVlLi4uDQptZWFuKHRyYWluX2RmJEFnZSkNCg0KI1doeT8gSXQgaGFzIG1pc3NpbmcgdmFsdWVzLi4uIFNvbHV0aW9uPyBSZW1vdmUgdGhlIG1pc3NpbmcgdmFsdWVzLiANCm1lYW4odHJhaW5fZGYkQWdlLCBuYS5ybT1UUlVFKQ0KYGBgDQoNCioqU3VtbWFyaXppbmcgYSBjYXRlZ29yaWNhbCB2YXJpYWJsZSoqDQpgYGB7cn0NCnRhYmxlKHRyYWluX2RmJFNleCkNCmBgYA0KDQoqKioNCiMjIyMgKipEYXRhIFZpc3VhbGl6YXRpb24qKg0KKioqDQoNCioqZ2dwbG90KioNCg0KZ2dwbG90IGlzIG9uZSBvZiB0aGUgbW9zdCBmYW1vdXMgbGlicmFyaWVzIG9mIFIgdGhhdCBpcyB1c2VkIGZvciBkYXRhIHZpc3VhbGl6YXRpb25zLiBJZiB5b3Ugd2FudCB0byBsZWFybiBtb3JlIGFib3V0IGdncGxvdHMsIGhlcmUgaXMgYSBbbGlua10oaHR0cHM6Ly9ya2FiYWNvZmYuZ2l0aHViLmlvL2RhdGF2aXMvSW50cm9HR1BMT1QuaHRtbCkgZm9yIHlvdS4NCg0KYGBge3J9DQpsaWJyYXJ5KGdncGxvdDIpDQpnZ3Bsb3QoZGF0YSA9IHRyYWluX2RmLA0KICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IFBhc3NlbmdlcklkLCB5ID0gRmFyZSkpDQpgYGANCg0KV2h5IGlzIHRoZSBncmFwaCBlbXB0eT8gV2Ugc3BlY2lmaWVkIHRoYXQgdGhlIFBhc3NlbmdlcklkIHZhcmlhYmxlIHNob3VsZCBiZSBtYXBwZWQgdG8gdGhlIHgtYXhpcyBhbmQgdGhhdCB0aGUgRmFyZSBzaG91bGQgYmUgbWFwcGVkIHRvIHRoZSB5LWF4aXMsIGJ1dCB3ZSBoYXZlbuKAmXQgeWV0IHNwZWNpZmllZCB3aGF0IHdlIHdhbnRlZCBwbGFjZWQgb24gdGhlIGdyYXBoLg0KDQpHZW9tcyB0byB0aGUgcmVzY3VlIQ0KDQpgYGB7cn0NCmxpYnJhcnkoZ2dwbG90MikNCmdncGxvdChkYXRhID0gdHJhaW5fZGYsDQogICAgICAgbWFwcGluZyA9IGFlcyh4ID0gUGFzc2VuZ2VySWQsIHkgPSBGYXJlKSkrDQogIGdlb21fcG9pbnQoKQ0KYGBgDQoNCkxldCdzIG1ha2UgaXQgZmFuY3kgYW5kIGZpdCBhIGxpbmUhDQoNCmBgYHtyfQ0KbGlicmFyeShnZ3Bsb3QyKQ0KZ2dwbG90KGRhdGEgPSB0cmFpbl9kZiwNCiAgICAgICBtYXBwaW5nID0gYWVzKHggPSBQYXNzZW5nZXJJZCwgeSA9IEZhcmUpKSsNCiAgZ2VvbV9wb2ludChjb2xvciA9ICJjb3JuZmxvd2VyYmx1ZSIsDQogICAgICAgICAgICAgYWxwaGEgPSAuNywNCiAgICAgICAgICAgICBzaXplID0gMykrDQogICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iKQ0KYGBgDQoNCg0KKipIaXN0b2dyYW1zKioNCg0KYGBge3J9DQpsaWJyYXJ5KHNjYWxlcykNCmdncGxvdCh0cmFpbl9kZiwgDQogICAgICAgYWVzKHggPSBBZ2UsIA0KICAgICAgICAgICB5PSAuLmNvdW50Li4gLyBzdW0oLi5jb3VudC4uKSkpICsNCiAgZ2VvbV9oaXN0b2dyYW0oZmlsbCA9ICJCbHVlIiwgDQogICAgICAgICAgICAgICAgIGNvbG9yID0gIndoaXRlIiwgDQogICAgICAgICAgICAgICAgIGJpbndpZHRoID0gNSkgKyANCiAgbGFicyh0aXRsZT0iVHJhdmVsbGVycyBieSBhZ2UiLCANCiAgICAgICB5ID0gIlBlcmNlbnQiLA0KICAgICAgIHggPSAiQWdlIikgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gcGVyY2VudCkNCmBgYA0KDQoqKkNhdGVnb3JpY2FsIGRhdGEqKg0KYGBge3J9DQpnZ3Bsb3QodHJhaW5fZGYsIGFlcyh4ID0gU2V4KSkgKyANCiAgZ2VvbV9iYXIoZmlsbCA9ICJjb3JuZmxvd2VyYmx1ZSIsIA0KICAgICAgICAgICBjb2xvcj0iYmxhY2siKSArDQogIGxhYnMoeCA9ICJTZXgiLCANCiAgICAgICB5ID0gIkZyZXF1ZW5jeSIsIA0KICAgICAgIHRpdGxlID0gIlRyYXZlbGxlcnMgYnkgU2V4IikrDQogIGNvb3JkX2ZsaXAoKQ0KYGBgDQoNCioqUGllIGNoYXJ0KioNCg0KYGBge3J9DQojIGNyZWF0ZSBhIHBpZSBjaGFydCB3aXRoIHNsaWNlIGxhYmVscw0KcGxvdGRhdGEgPC0gdHJhaW5fZGYgJT4lDQogIGNvdW50KFBjbGFzcykgJT4lDQogIGFycmFuZ2UoZGVzYyhQY2xhc3MpKSAlPiUNCiAgbXV0YXRlKHByb3AgPSByb3VuZChuKjEwMC9zdW0obiksIDEpLA0KICAgICAgICAgbGFiLnlwb3MgPSBjdW1zdW0ocHJvcCkgLSAwLjUqcHJvcCkNCg0KcGxvdGRhdGEkbGFiZWwgPC0gcGFzdGUwKHBsb3RkYXRhJFBjbGFzcywgIlxuIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICByb3VuZChwbG90ZGF0YSRwcm9wKSwgIiUiKQ0KDQpnZ3Bsb3QocGxvdGRhdGEsIA0KICAgICAgIGFlcyh4ID0gIiIsIA0KICAgICAgICAgICB5ID0gcHJvcCwgDQogICAgICAgICAgIGZpbGwgPSBQY2xhc3MpKSArDQogIGdlb21fYmFyKHdpZHRoID0gMSwgDQogICAgICAgICAgIHN0YXQgPSAiaWRlbnRpdHkiLCANCiAgICAgICAgICAgY29sb3IgPSAiYmxhY2siKSArDQogIGdlb21fdGV4dChhZXMoeSA9IGxhYi55cG9zLCBsYWJlbCA9IGxhYmVsKSwgDQogICAgICAgICAgICBjb2xvciA9ICJibGFjayIpICsNCiAgY29vcmRfcG9sYXIoInkiLCANCiAgICAgICAgICAgICAgc3RhcnQgPSAwLCANCiAgICAgICAgICAgICAgZGlyZWN0aW9uID0gLTEpICsNCiAgdGhlbWVfdm9pZCgpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIkZBTFNFIikgKw0KICBsYWJzKHRpdGxlID0gIlBhcnRpY2lwYW50cyBieSBjbGFzcyIpDQpgYGANCg0KKipUcmVlIG1hcCoqDQoNCmBgYHtyfQ0KbGlicmFyeSh0cmVlbWFwaWZ5KQ0KDQojIGNyZWF0ZSBhIHRyZWVtYXAgb2YgbWFycmlhZ2Ugb2ZmaWNpYWxzDQpwbG90ZGF0YSA8LSB0cmFpbl9kZiAlPiUNCiAgY291bnQoUGNsYXNzKQ0KDQpnZ3Bsb3QocGxvdGRhdGEsIA0KICAgICAgIGFlcyhmaWxsID0gUGNsYXNzLCANCiAgICAgICAgICAgYXJlYSA9IG4sDQogICAgICAgICAgIGxhYmVsID0gUGNsYXNzKSkgKw0KICBnZW9tX3RyZWVtYXAoKSArIA0KICBnZW9tX3RyZWVtYXBfdGV4dChjb2xvdXIgPSAid2hpdGUiLCANCiAgICAgICAgICAgICAgICAgICAgcGxhY2UgPSAiY2VudHJlIikgKw0KICAgbGFicyh0aXRsZSA9ICJUcmFpbmluZyBkYXRhIGJ5IGNsYXNzIikgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQpgYGANCg0KKipWaXN1YWxpemluZyAzIHZhcmlhYmxlcyoqDQoNCmBgYHtyfQ0KIyBwbG90IGZhcmUgaGlzdG9ncmFtcyBieSBQY2xhc3MNCmdncGxvdCh0cmFpbl9kZiwgYWVzKHggPSBGYXJlKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShmaWxsID0gImNvcm5mbG93ZXJibHVlIiwNCiAgICAgICAgICAgICAgICAgY29sb3IgPSAid2hpdGUiKSArDQogIGZhY2V0X3dyYXAoflBjbGFzcywgbmNvbCA9IDEpICsNCiAgbGFicyh0aXRsZSA9ICJGYXJlIGhpc3RvZ3JhbXMgYnkgUGNsYXNzIikNCmBgYA0KDQpgYGB7cn0NCiMgcGxvdCBmYXJlIGhpc3RvZ3JhbXMgYnkgc2V4IGFuZCBwY2xhc3MNCmdncGxvdCh0cmFpbl9kZiwgYWVzKHggPSBGYXJlKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShjb2xvciA9ICJ3aGl0ZSIsDQogICAgICAgICAgICAgICAgIGZpbGwgPSAiY29ybmZsb3dlcmJsdWUiKSArDQogIGZhY2V0X2dyaWQoU2V4IH4gUGNsYXNzKSArDQogIGxhYnModGl0bGUgPSAiRmFyZSBoaXN0b2dyYW1zIGJ5IHNleCBhbmQgcGNsYXNzIiwNCiAgICAgICB4ID0gIkZhcmUiKQ0KYGBgDQoNCioqTWlzY2VsbGFuZW91cyBncmFwaHMqKg0KDQpgYGB7cn0NCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkocGxvdGx5KQ0KDQpwIDwtIGdncGxvdCh0cmFpbl9kZiwgYWVzKHg9UGFzc2VuZ2VySWQsIA0KICAgICAgICAgICAgICAgICAgICAgeT1GYXJlLCANCiAgICAgICAgICAgICAgICAgICAgIGNvbG9yPVBjbGFzcykpICsNCiAgZ2VvbV9wb2ludChzaXplPTMpICsNCiAgbGFicyh4ID0gIlBhc3NlbmdlciBJZCIsDQogICAgICAgeSA9ICJGYXJlIiwNCiAgICAgICBjb2xvciA9ICJQYXNzZW5nZXIgQ2xhc3MiKSArDQogIHRoZW1lX2J3KCkNCg0KZ2dwbG90bHkocCkNCmBgYA0KDQoNCioqKg0KIyMjIyAqKkRhdGEgQW5hbHlzaXMqKg0KKioqDQoNCiMjIyMjICoqTmV1cmFsIE5ldHdvcmtzKioNCg0KSW50ZXJlc3RpbmcgdmlkZW8gdGhhdCBiZWF1dGlmdWxseSBleHBsYWlucyB3aGF0IGEgbmV1cmFsIG5ldHdvcmsgaXMuLi4gW2xpbmtdKGh0dHBzOi8vd3d3LnlvdXR1YmUuY29tL3dhdGNoP3Y9YWlyY0FydXZuS2spDQoNCmBgYHtyfQ0KIyBsb2FkIGxpYnJhcnkNCnJlcXVpcmUobmV1cmFsbmV0KQ0KDQptIDwtIG1vZGVsLm1hdHJpeCggDQogIH4gU3Vydml2ZWQgKyBQY2xhc3MgKyBTZXggKyBBZ2UgKyBTaWJTcCArIFBhcmNoICsgRmFyZSArIEVtYmFya2VkLCANCiAgZGF0YSA9IHRyYWluX2RmIA0KKQ0KaGVhZChtKQ0KbGlicmFyeShuZXVyYWxuZXQpDQpyIDwtIG5ldXJhbG5ldCggDQogIFN1cnZpdmVkIH4gUGNsYXNzICsgU2V4bWFsZSArIEFnZSArIFNpYlNwICsgUGFyY2ggKyBGYXJlICsgRW1iYXJrZWRDICsgRW1iYXJrZWRRICsgRW1iYXJrZWRTLCANCiAgZGF0YT1tLCBoaWRkZW49MTAsIHRocmVzaG9sZD0wLjUNCikNCmBgYA0KDQpgYGB7cn0NCnBsb3QocikNCmBgYA0KDQpgYGB7cn0NCm0yIDwtIG1vZGVsLm1hdHJpeCggDQogIH4gUGNsYXNzICsgU2V4ICsgQWdlICsgU2liU3AgKyBQYXJjaCArIEZhcmUgKyBFbWJhcmtlZCwgDQogIGRhdGEgPSB0ZXN0X2RmIA0KKQ0KDQpQcmVkaWN0PWNvbXB1dGUocixtMikNCnByb2IgPC0gUHJlZGljdCRuZXQucmVzdWx0DQpOZXdfdGVzdF9kZiA8LXRlc3RfZGYgJT4lIGRyb3BfbmEoUGNsYXNzLCBTZXgsIEFnZSxTaWJTcCxQYXJjaCwgRmFyZSxFbWJhcmtlZCkNCk5ld190ZXN0X2RmJFN1cnZpdmVkIDwtaWZlbHNlKHByb2I+MC41LCAxLCAwKQ0Kd3JpdGUuY3N2KE5ld190ZXN0X2RmLCJSZXN1bHQuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpDQoNCmBgYA0KDQoNCiMjIyMjICoqUHJpbmNpcGFsIENvbXBvbmVudCBBbmFseXNpcyAoUENBKSoqDQpQQ0EgaXMgYSBmYW1vdXMgZGltZW5zaW9uYWxpdHkgcmVkdWN0aW9uIHRlY2huaXF1ZSBhbmQgeW91IHdhbnQgdG8gZmluZCB0aGF0IHdoaWNoIGNvbXBvbmVudC9zZXQgb2YgY29tcG9uZW50cyBpbnRyb2R1Y2UgdGhlIG1vc3QgdmFyaWF0aW9uIGluIHlvdXIgcmVzcG9uc2UgdmFyaWFibGUuIEluIG90aGVyIHdvcmRzLCB3ZSBhcmUgaW50ZXJlc3RlZCBpbiBmaW5kaW5nIHRob3NlIHZhcmlhYmxlcyB3aGljaCBhcmUgbWF0aGVtYXRpY2FsbHkgdGhlIHJpY2hlc3QgaW4gdGVybXMgb2YgaW5mb3JtYXRpb24uIA0KDQpOb3RlOiBQQ0EgY2FuIG9ubHkgYmUgcnVuIG9uIGNvbnRpbnVvdXMgdmFyaWFibGVzLiANCmBgYHtyfQ0KbGlicmFyeSh0aWR5cikNCnRyYWluX3BjYV9kZiA8LSBzZWxlY3QodHJhaW5fZGYsIEFnZSwgU2liU3AsIFBhcmNoLCBGYXJlKQ0KdHJhaW5fcGNhX2RmIDwtIHRyYWluX3BjYV9kZiAlPiUgZHJvcF9uYSgpDQpwY2FfcmVzdWx0IDwtIHByY29tcCh0cmFpbl9wY2FfZGYsIGNlbnRlciA9IFRSVUUsc2NhbGUuID0gVFJVRSkNCnN1bW1hcnkocGNhX3Jlc3VsdCkNCmBgYA0KDQoqKkludGVycHJldGF0aW9uOioqDQoNCisgNDAuOTIlIG9mIHRoZSB0b3RhbCB2YXJpYXRpb24gaW4gZGF0YSBjYW4gYmUgZXhwbGFpbmVkIGJ5IGFnZS4NCisgMjcuNjglIG9mIHRoZSB0b3RhbCB2YXJpYXRpb24gaW4gZGF0YSBjYW4gYmUgZXhwbGFpbmVkIGJ5IFNpYlNwDQorIDE2LjczJSBvZiB0aGUgdG90YWwgdmFyaWF0aW9uIGluIGRhdGEgY2FuIGJlIGV4cGxhaW5lZCBieSBQYXJjaC4NCisgMTQuNjclIG9mIHRoZSB0b3RhbCB2YXJpYXRpb24gaW4gZGF0YSBjYW4gYmUgZXhwbGFpbmVkIGJ5IEZhcmUuDQoNCkkgd2lsbCBkZWZpbml0ZWx5IGtlZXAgYWdlIGluIHRoZSBtb2RlbCwgd2hlbiBJIGFtIHVzaW5nIG90aGVyIG1hY2hpbmUgbGVhcm5pbmcgYWxnb3JpdGhtcyBiZWNhdXNlIHRoZSB2YXJpYWJsZSBhZ2UgaW50cm9kdWNlcyB0aGUgaGlnaGVzdCBwZXJjZW50YWdlIG9mIHRvdGFsIHZhcmlhdGlvbiBpbiB0aGUgZ2l2ZW4gZGF0YXNldC4NCg0K